package com.googlecode.yoohoo.xmppcore.protocol.parsing.error;

import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import com.googlecode.yoohoo.xmppcore.protocol.XmppProtocolException;
import com.googlecode.yoohoo.xmppcore.protocol.parsing.IXmppParsingFactory;
import com.googlecode.yoohoo.xmppcore.protocol.stanza.Iq;
import com.googlecode.yoohoo.xmppcore.protocol.stanza.Message;
import com.googlecode.yoohoo.xmppcore.protocol.stanza.Presence;
import com.googlecode.yoohoo.xmppcore.protocol.stanza.Stanza;
import com.googlecode.yoohoo.xmppcore.protocol.stanza.StanzaKind;
import com.googlecode.yoohoo.xmppcore.protocol.stanza.error.BadRequest;
import com.googlecode.yoohoo.xmppcore.protocol.stanza.error.StanzaError;

public class StanzaErrorParser implements IStanzaErrorParser {
	public static final String ERROR_TYPE_STRING = "error";
	
	@Override
	public StanzaError parse(IXmppParsingFactory parsingFactory, XMLStreamReader reader,
			String xml, Stanza stanza) throws XMLStreamException {
		StanzaKind stanzaKind = null;
		if (stanza instanceof Message) {
			stanzaKind = StanzaKind.MESSAGE;
		} else if (stanza instanceof Presence) {
			stanzaKind = StanzaKind.PRESENCE;
		} else if (stanza instanceof Iq) {
			stanzaKind = StanzaKind.IQ;
		} else {
			throw new XmppProtocolException(new BadRequest("Invalid stanza type."));
		}
				
		String senderXml = null;
		int start = 0;
		start = reader.getLocation().getCharacterOffset();
		int eventType = reader.next();
		if (eventType == XMLStreamConstants.START_ELEMENT)  {
			if ("".equals(reader.getPrefix()) && reader.getNamespaceURI() == null &&
					stanzaKind.toString().equals(reader.getLocalName())) {
				senderXml = readSenderXml(reader, xml, start);
			}
		} else {
			throw new XmppProtocolException(new BadRequest("XML parsing error. Invalid XML event type."));
		}
		
		String errorXml = null;
		if (senderXml != null) {
			start = reader.getLocation().getCharacterOffset();
		}
		
		int end = 0;
		while (true) {
			end = reader.getLocation().getCharacterOffset();
			eventType = reader.next();
			if (eventType == XMLStreamConstants.END_ELEMENT) {
				if ("".equals(reader.getPrefix()) && reader.getNamespaceURI() == null &&
						stanzaKind.toString().equals(reader.getLocalName())) {
					errorXml = xml.substring(start, end);
					break;
				}
			} else {
				continue;
			}
		}

		Object obj = parsingFactory.parse(errorXml);
		if (obj instanceof StanzaError) {
			StanzaError error = (StanzaError)obj;
			
			error.setStanzaKind(stanzaKind);
			
			error.setFrom(stanza.getFrom());
			error.setTo(stanza.getTo());
			error.setId(stanza.getId());
			error.setType(ERROR_TYPE_STRING);
			error.setXmlLang(stanza.getXmlLang());
			
			error.setSenderXml(senderXml);
			
			return error;
		}
		
		throw new XmppProtocolException(new BadRequest("???Not an valid stanza error???"));
	}
	
	private String readSenderXml(XMLStreamReader reader, String xml, int start) throws XMLStreamException {
		String kindString = reader.getLocalName();
		while (true) {
			int eventType = reader.next();
			if (eventType != XMLStreamConstants.END_ELEMENT)
				continue;
			
			if ("".equals(reader.getPrefix()) && reader.getNamespaceURI() == null &&
					kindString.equals(reader.getLocalName())) {
				Location end = reader.getLocation();
				
				return xml.substring(start, end.getCharacterOffset());
			}
		}
	}
}
